#include "userprog/syscall.h"
#include <stdio.h>
#include <syscall-nr.h>
#include "threads/interrupt.h"
#include "threads/thread.h"
#include "threads/vaddr.h"
#include "userprog/pagedir.h"
#include "threads/init.h"
#include "devices/shutdown.h" /* Imports shutdown_power_off() for use in halt(). */
#include "filesys/file.h"
#include "filesys/filesys.h"
#include "userprog/process.h"
#include "devices/input.h"
#include "threads/malloc.h"

static void syscall_handler(struct intr_frame *);

struct thread_file * find_file(int fd);
bool sys_create (const char *file, unsigned initial_size);
bool sys_remove(const char *file);
unsigned sys_tell(int fd);

static int 
get_user (const uint8_t *uaddr)
{
  int result;
  asm ("movl $1f, %0; movzbl %1, %0; 1:" : "=&a" (result) : "m" (*uaddr));
  return result;
}

void get_stack_arguments(struct intr_frame *f, int *args, int num_of_args);

void *check_phys_page_ptr(const void *ptr_to_check);

void syscall_init(void)
{
  intr_register_int(0x30, 3, INTR_ON, syscall_handler, "syscall");
}

static void
syscall_handler(struct intr_frame *f)
{
  /* 首先确保系统调用参数是一个有效地址。如果不是，立即退出。 */
  check_valid_addr((const void *)f->esp);

  /* 保存直接跟在系统调用之后的栈参数。 */
  int args[3];

  /*存放物理页地址*/
  void *phys_page_ptr;

  int syscall_number = *(int *)f->esp;

  switch (syscall_number)
  {
  case SYS_HALT:
  {
    sys_halt();
    break;
  }

  case SYS_EXIT:
  {
    get_stack_arguments(f, &args[0], 1);
    sys_exit(args[0]);
    break;
  }

  case SYS_EXEC:
  {
    get_stack_arguments(f, &args[0], 1);
    phys_page_ptr = check_phys_page_ptr((const void *)args[0]);
    const char *cmd_line = (const char *)phys_page_ptr;
    // exec()返回值保存至eax
    f->eax = sys_exec(cmd_line);
    break;
  }

  case SYS_WAIT:
  {
    get_stack_arguments(f, &args[0], 1); // 当前进程必须等待的子进程的 PID（进程标识符）。
    f->eax = sys_wait((pid_t)args[0]);
    break;
  }

  case SYS_CREATE:
  {
    // 第一个参数是正在创建的文件的名称，第二个参数是文件的大小
    get_stack_arguments(f, &args[0], 2);
    check_buffer((void *)args[0], args[1]);

    // 确保转换后的地址有效
    phys_page_ptr = check_phys_page_ptr((const void *)args[0]);
    const char *file = (const char *)phys_page_ptr;
    unsigned file_size = (unsigned)args[1];

    f->eax = sys_create(file, file_size);
    break;
  }
  case SYS_REMOVE:
  {
    // 第一个参数是文件的名称
    get_stack_arguments(f, &args[0], 1);

    // 确保转换后的地址有效
    phys_page_ptr = check_phys_page_ptr((const void *)args[0]);
    const char *file = (const char *)phys_page_ptr;

    f->eax = sys_remove(file);
    break;
  }
  case SYS_OPEN:
  {
    get_stack_arguments(f, &args[0], 1);
    phys_page_ptr = check_phys_page_ptr((const void *)args[0]);
    const char *file = (const char *)phys_page_ptr;
    f->eax = sys_open(file);
    break;
  }

  case SYS_FILESIZE:
  {
    // 参数是fd
    get_stack_arguments(f, &args[0], 1);
    f->eax = sys_filesize(args[0]);
    break;
  }
  case SYS_READ:
  {
    // 第一个参数表示文件描述符，第二个参数表示缓冲区，第三个参数表示缓冲区长度
    get_stack_arguments(f, &args[0], 3);

    check_buffer((void *)args[1], args[2]);

    phys_page_ptr = check_phys_page_ptr((const void *)args[1]);
    void *buffer = (void *)phys_page_ptr;
    unsigned buffer_length = (unsigned)args[2];
    int fd = args[0];

    f->eax = sys_read(fd, buffer, buffer_length);
    break;
  }
  case SYS_WRITE:
  {
    /* 从栈中获取三个参数。第一个参数表示文件描述符，第二个参数表示缓冲区，第三个参数表示缓冲区长度。 */
    get_stack_arguments(f, &args[0], 3);
    check_buffer((void *)args[1], args[2]);
    phys_page_ptr = check_phys_page_ptr((const void *)args[1]);
    f->eax = sys_write(args[0], (const void *)phys_page_ptr, (unsigned)args[2]);
    break;
  }

  case SYS_SEEK:
  {
    // 第一个fd,第二个位置
    get_stack_arguments(f, &args[0], 2);
    sys_seek(args[0], (unsigned)args[1]);
    break;
  }
  case SYS_TELL:
  {
    get_stack_arguments(f, &args[0], 1); // 获取fd文件描述符
    f->eax = sys_tell(args[0]);
    break;
  }
  case SYS_CLOSE:
  {
    get_stack_arguments(f, &args[0], 1); // 获取fd文件描述符
    sys_close(args[0]);
    break;
  }
  default:
    printf("[ERROR] system call %d is unimplemented!\n", syscall_number);
    sys_exit(-1);
    break;
  }
}

void sys_halt(void)
{
  shutdown_power_off();
}

void sys_exit(int status)
{
  thread_current()->pcb->exitcode = status;
  //printf("%s: exit(%d)\n", thread_current()->name, status);
  thread_exit();
}

pid_t sys_exec(const char *cmd_line)
{
  if (!cmd_line)
  {
    return -1;
  }
  acquire_file_lock();
  pid_t child_tid = process_execute(cmd_line);
  release_file_lock();
  return child_tid;
}

int sys_write(int fd, const void *buffer, unsigned length)
{
  struct list_elem *temp; // 用于遍历文件描述符列表的链表节点

  acquire_file_lock();

  // 若 fd == 1，则使用 putbuf() 输出到控制台
  if (fd == 1)
  {
    putbuf(buffer, length);
    release_file_lock();
    return length;
  }
  // 如果标准输入或没有文件
  if (fd == 0 || list_empty(&thread_current()->file_list))
  {
    release_file_lock();
    return 0;
  }

  /*检查给定的fd是否已打开并归当前进程所有。如果是，返回写入文件的字节数。 */
  for (temp = list_begin (&thread_current()->file_list); temp != list_end (&thread_current()->file_list); temp = list_next (temp))
  {
    struct thread_file *t = list_entry(temp, struct thread_file, file_elem);
    if (t->fd == fd)
    {
      int bytes_written = (int)file_write(t->file, buffer, length);
      release_file_lock();
      return bytes_written;
    }
  }

  release_file_lock();

  /* 无法写入文件，return 0 */
  return 0;
}

int sys_wait(pid_t pid)
{
  return process_wait(pid);
}

bool sys_create(const char *file, unsigned initial_size)
{
  acquire_file_lock();
  bool if_created = filesys_create(file, initial_size);
  release_file_lock();
  return if_created;
}

bool sys_remove(const char *file)
{
  acquire_file_lock();
  bool was_removed = filesys_remove(file);
  release_file_lock();
  return was_removed;
}

/*打开 file，返回一个文件描述符（fd）*/
int sys_open(const char *file)
{
  acquire_file_lock();

  struct file *f = filesys_open(file);

  /* 如果失败，返回 -1 */
  if (f == NULL)
  {
    release_file_lock();
    return -1;
  }

  /* 创建一个结构体来保存文件/文件描述符，以便在当前进程的列表中使用。
     增加文件描述符的值以供后续文件使用。释放锁并返回文件描述符作为整数。 */
  struct thread_file *new_file = malloc(sizeof(struct thread_file));
  new_file->file = f;
  int fd = thread_current()->next_fd;
  thread_current()->next_fd++;
  new_file->fd = fd;
  list_push_front(&thread_current()->file_list, &new_file->file_elem);
  release_file_lock();
  return fd;
}

int sys_filesize(int fd)
{
  struct list_elem *temp;

  acquire_file_lock();

  if (list_empty(&thread_current()->file_list))
  {
    release_file_lock();
    return -1;
  }

  for (temp = list_begin (&thread_current()->file_list); temp != list_end (&thread_current()->file_list); temp = list_next (temp))
  {
    struct thread_file *t = list_entry(temp, struct thread_file, file_elem);
    if (t->fd == fd)
    {
      release_file_lock();
      return (int)file_length(t->file);
    }
  }

  release_file_lock();

  return -1;
}

int sys_read(int fd, void *buffer, unsigned length)
{
  struct list_elem *temp;

  acquire_file_lock();

  // 如果 fd == 0，则从键盘读取，使用 input_getc()
  if (fd == 0)
  {
    release_file_lock();
    return (int)input_getc();
  }

  // 如果是标准输入或没有文件
  if (fd == 1 || list_empty(&thread_current()->file_list))
  {
    release_file_lock();
    return 0;
  }

  for (temp = list_begin (&thread_current()->file_list); temp != list_end (&thread_current()->file_list); temp = list_next (temp))
  {
    struct thread_file *t = list_entry(temp, struct thread_file, file_elem);
    if (t->fd == fd)
    {
      release_file_lock();
      int bytes = (int)file_read(t->file, buffer, length);
      return bytes;
    }
  }

  release_file_lock();

  return -1;
}

void sys_seek(int fd, unsigned position)
{
  struct list_elem *temp;

  acquire_file_lock();

  if (list_empty(&thread_current()->file_list))
  {
    release_file_lock();
    return;
  }

  for (temp = list_begin (&thread_current()->file_list); temp != list_end (&thread_current()->file_list); temp = list_next (temp))
  {
    struct thread_file *t = list_entry(temp, struct thread_file, file_elem);
    if (t->fd == fd)
    {
      file_seek(t->file, position);
      release_file_lock();
      return;
    }
  }

  release_file_lock();

  return;
}

unsigned sys_tell(int fd)
{
  struct list_elem *temp;

  acquire_file_lock();

  if (list_empty(&thread_current()->file_list))
  {
    release_file_lock();
    return -1;
  }

  for (temp = list_begin (&thread_current()->file_list); temp != list_end (&thread_current()->file_list); temp = list_next (temp))
  {
    struct thread_file *t = list_entry(temp, struct thread_file, file_elem);
    if (t->fd == fd)
    {
      unsigned position = (unsigned)file_tell(t->file);
      release_file_lock();
      return position;
    }
  }

  release_file_lock();

  return -1;
}

void sys_close(int fd)
{
  struct list_elem *temp;

  acquire_file_lock();

  if (list_empty(&thread_current()->file_list))
  {
    release_file_lock();
    return;
  }

  for (temp = list_begin (&thread_current()->file_list); temp != list_end (&thread_current()->file_list); temp = list_next (temp))
  {
    struct thread_file *t = list_entry(temp, struct thread_file, file_elem);
    if (t->fd == fd)
    {
      file_close(t->file);
      list_remove(&t->file_elem);
      release_file_lock();
      free(t);
      return;
    }
  }

  release_file_lock();

  return;
}

void check_valid_addr(const void *ptr_to_check)
{
  /* 如果传入的参数不在用户地址空间，则以退出状态-1终止程序。 */
  if (!is_user_vaddr(ptr_to_check))
  {
    /* 终止程序并释放其资源 */
    sys_exit(-1);
  }
}

/* 确保给定缓冲区中的每个内存地址都在有效的用户空间中。 */
void check_buffer(void *buff_to_check, unsigned size)
{
  unsigned i;
  char *ptr = (char *)buff_to_check;
  for (i = 0; i < size; i++)
  {
    check_valid_addr((const void *)ptr);
    ptr++;
  }
}

/* 从程序的栈中获取最多三个参数（它们直接跟在系统调用参数之后）。 */
void get_stack_arguments(struct intr_frame *f, int *args, int num_of_args)
{
  int i;
  int *ptr;
  for (i = 0; i < num_of_args; i++)
  {
    ptr = (int *)f->esp + i + 1;
    check_valid_addr((const void *)ptr);
    args[i] = *ptr;
  }
}

/* 检查给定指针是否指向有效的物理内存页。如果是，则返回指向该页的指针。如果不是，则以退出状态-1终止程序。*/
void *check_phys_page_ptr(const void *ptr_to_check)
{
  void *phys_page_ptr = (void *)pagedir_get_page(thread_current()->pagedir, ptr_to_check);
  if (!phys_page_ptr) // 转换后检查是否为有效地址
  {
    sys_exit(-1);
  }
  /* 检查页有效 */
  uint8_t *check_byteptr = (uint8_t *) ptr_to_check;
  for (uint8_t i = 0; i < 4; i++) 
  {
    if (get_user(check_byteptr + i) == -1)
    {
      sys_exit(-1);
    }
  }
  return phys_page_ptr;
}

struct thread_file * 
find_file (int fd)
{
  struct list_elem *e;
  struct thread_file * temp = NULL;
  struct list *files = &thread_current ()->file_list;
  for (e = list_begin (files); e != list_end (files); e = list_next (e)){
    temp = list_entry (e, struct thread_file, file_elem);
    if (fd == temp->fd)
      return temp;
  }
  return NULL;
}